import { defineComponent, type Component, type Entity, type IWorld, hasComponent, Types } from '$lib/ecs';
import { log } from '$lib/log';
import { decode, encode } from '$lib/util';
import { getStamina, setStamina } from './stamina';
import { getEquippedIn } from './equipment';
import { getSkillLevel } from './skill';
import { applyHeartDelay } from '$lib/content/heart';
import { distribute, random } from '$lib/random';
import { applyBleed, enumBodyParts } from './body';
import { getDodge } from './dodge';
import { getAccuracy } from './accuracy';

export const SelectedMeleeStyle = defineComponent('SelectedMeleeStyle', {
  style: [Types.ui8, 32],
});

export const setSelectedMeleeStyle = (world: IWorld, e: Entity, style: string) => {
  SelectedMeleeStyle.style[e] = encode(style);
};

export const getSelectedMeleeStyle = (world: IWorld, e: Entity) => {
  return decode(SelectedMeleeStyle.style[e]);
};

export type MeleeStyle = {
  name: string,
  stamina: number,
  delay: number,
  skills: Component[],
  weaponReq: Component[],
  bodyPartTargetting?: Map<Component, number>,
};

const meleeStyles = new Map<string, MeleeStyle>();

export const registerMeleeStyle = (style: MeleeStyle) => meleeStyles.set(style.name, style);

export const getMeleeStyle = (name: string) => meleeStyles.get(name);

export const enumAvaliableMeleeStyles = (world: IWorld, e: Entity) => {
  return Array.from(meleeStyles.entries()).filter(([_, style]) => canUseMeleeStyle(world, e, style));
};

export const canUseMeleeStyle = (world: IWorld, e: Entity, style: MeleeStyle) => {
  return style.skills.every((skill) => getSkillLevel(world, e, skill) > 0);
};

const checkStyle = (world: IWorld, style: MeleeStyle, weapon: Entity) => {
  if (!style.weaponReq.every((comp) => hasComponent(world, comp, weapon)))
    return false;
  return true;
};

const getStyleBodyPartTargetting = (world: IWorld, style: MeleeStyle, target: Entity) => {
  return enumBodyParts(world, target).map((bodyPart) =>
    [bodyPart, style.bodyPartTargetting?.get(bodyPart) ?? 1]);
};

export const meleeAttack = (world: IWorld, e: Entity, target: Entity) => {
  // check weapon & fighting style
  const style = getMeleeStyle(getSelectedMeleeStyle(world, e));
  if (style === undefined) {
    log("No selected melee style!");
    return false;
  }
  const weapon = getEquippedIn(world, e, 'mainhand');
  const ok = checkStyle(world, style, weapon);
  if (!ok) {
    log("Selected style is incompatible with current weapon!");
    return false;
  }
  const stamina = getStamina(world, e).amount;
  if (stamina < style.stamina) {
    log("Not enough stamina!");
    return false;
  }
  const bodyPart = distribute(random(), getStyleBodyPartTargetting(world, style, target));
  const accuracy = getAccuracy(world, e, style, weapon);
  const dodge = getDodge(world, target);
  if (random() * accuracy >= random() * dodge) {
    // TODO
    applyBleed(world, target, bodyPart, 1);
  }
  applyHeartDelay(world, e, style.delay);
  setStamina(world, e, stamina - style.stamina);
  return true;
};

export const MeleeAttack = (target: Entity) => (world: IWorld, e: Entity) => {
  meleeAttack(world, e, target);
};
